//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include "ddktsttimer.h"
#include "ddktstserver.h"

class CDDKTstTimer : public CDeviceDriver
{
public:

    CARAPI Read(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ UInt32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ UInt32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose() {}

private:
    DzTimer     **m_ppTimer;
    uint_t      m_uAccount;
    uint_t      *m_puDueTick;
    int         *m_pnPara;
};

int g_nCBCount = 0;
int g_nRightCount = 0;

EXTERN IDeviceDriver * CDECL CreateDDKTstTimer(uint_t uDeviceNo, void *pvParameter)
{
    CDDKTstTimer *pDriver = new CDDKTstTimer;
    if (NULL == pDriver) {
        kprintf("ERROR: new CDDKTstMemory: Not enough memory!\n%s, %d\n",
            __FILE__, __LINE__);
        return NULL;
    }
    kprintf("CreateDDKTstTimer\n");

    pDriver->AddRef();

    return pDriver;
}

ECode CDDKTstTimer::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CDDKTstTimer::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void CDECL TimerCallBack0(void *pvParameter)
{
    g_nCBCount += 100;

    kprintf("TimerCallBack0\n");
}

void CDECL TimerCallBack1(void *pvParameter)
{
    g_nCBCount++;

    if (pvParameter && (*(int*)pvParameter == g_nCBCount - 100)) {
        g_nRightCount++;
    }
    else {
        kprintf("param error!\n");
    }

    kprintf("TimerCallBack1. \n");
}

ECode CDDKTstTimer::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (NULL != ppCompletionEvent) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    switch (nControlCode) {
        case TmrTst_DzTimer:{
            PControlPack pControlPack = (PControlPack)inBuffer.GetPayload();
            if (pControlPack == NULL) {
                return E_INVALID_ARGUMENT;
            }

            m_uAccount = pControlPack->uTimerCount;
            m_puDueTick = new uint_t[m_uAccount];
            if (NULL == m_puDueTick) {
                kprintf("0 No memory\n");
                return E_OUT_OF_MEMORY;
            }

            m_pnPara = new int[m_uAccount];
            if (NULL == m_pnPara) {
                kprintf("1 No memory\n");
                return E_OUT_OF_MEMORY;
            }
            for (uint_t i = 0; i < m_uAccount; i++) {
                m_pnPara[i] = i;
            }

            m_ppTimer = (DzTimer **)malloc(4 * m_uAccount);
            if (NULL == m_ppTimer) {
                kprintf("2 No memory\n");
                return E_OUT_OF_MEMORY;
            }

            m_puDueTick[0] = pControlPack->puRelativeDueTicks[0];
            m_ppTimer[0] = new DzTimer(m_puDueTick[0], TimerCallBack0, NULL);

            if (NULL == m_ppTimer[0]) {
                kprintf("3 No memroy\n");
                return E_OUT_OF_MEMORY;
            }

            for (uint_t i = 1; i < m_uAccount; i++) {
                m_puDueTick[i] = pControlPack->puRelativeDueTicks[i];
                m_ppTimer[i] = new DzTimer(m_puDueTick[i],
                                        TimerCallBack1, &m_pnPara[i]);

                if (NULL == m_ppTimer[i]) {
                    kprintf("3 No memroy\n");
                    return E_OUT_OF_MEMORY;
                }
            }

            break;
        }

        case TmrTst_Dispose:{
            if (pOutBuffer == NULL || inBuffer.IsNullOrEmpty()
                || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            delete m_puDueTick;
            delete m_pnPara;

            for (uint_t i = 0; i < m_uAccount; i++) {
                if (NULL != m_ppTimer[i]) {
                    delete m_ppTimer[i];
                }
            }

            if (NULL != m_ppTimer) {
                free(m_ppTimer);
            }

            break;
        }

        case TmrTst_Start:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            for (uint_t i = 0; i < m_uAccount; i++) {
                m_ppTimer[i]->Start();
            }

            break;
        }

        case TmrTst_Restart:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            for (uint_t i = 0; i < m_uAccount; i++) {
                m_ppTimer[i]->Restart(((uint_t *)inBuffer.GetPayload())[i]);
            }

            break;
        }

        case TmrTst_Cancel:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            for (uint_t i = 0; i < m_uAccount; i++) {
                m_ppTimer[i]->Cancel();
            }

            break;
        }

        case TmrTst_Due:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            if ((uint_t)pOutBuffer->GetCapacity() < m_uAccount * sizeof(bool_t)) {
                return E_OUT_OF_MEMORY;
            }

            uint_t i;
            for (i = 0; i < m_uAccount; i++) {
                ((bool_t *)pOutBuffer->GetPayload())[i] = m_ppTimer[i]->Due();
            }

            pOutBuffer->SetUsed(i * sizeof(bool_t));

            break;
        }

        case TmrTst_GetResidualTicks:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            if ((uint_t)pOutBuffer->GetCapacity() < m_uAccount * sizeof(uint_t)) {
                return E_OUT_OF_MEMORY;
            }

            uint_t i;

            for (i = 0; i < m_uAccount; i++) {
                ((uint_t *)pOutBuffer->GetPayload())[i] =
                                            m_ppTimer[i]->GetResidualTicks();
            }

            pOutBuffer->SetUsed(i * sizeof(uint_t));

            break;
        }

        case TmrTst_DzMillisecondsToTicks:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            *(uint_t *)pOutBuffer->GetPayload() = DzMillisecondsToTicks(
                    *(uint_t *)inBuffer.GetPayload());
            pOutBuffer->SetUsed(sizeof(uint_t));

            break;
        }

        case TmrTst_DzTicksToMilliseconds:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            *(uint_t *)pOutBuffer->GetPayload() = DzTicksToMilliseconds(
                    *(uint_t *)inBuffer.GetPayload());
            pOutBuffer->SetUsed(sizeof(uint_t));

            break;
        }

        case TmrTst_DzGetSystemTime:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            DzGetSystemTime((SystemTime *)pOutBuffer->GetPayload());
            pOutBuffer->SetUsed(sizeof(SystemTime));

            break;
        }

        case TmrTst_DzSetSystemTime:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }

            DzSetSystemTime(*(SystemTime *)inBuffer.GetPayload());
            pOutBuffer->SetUsed(sizeof(SystemTime));

            break;
        }

        case TmrTst_Result:{
            if (pOutBuffer == NULL ||
                inBuffer.IsNullOrEmpty() || pOutBuffer->GetCapacity()) {
                return E_INVALID_ARGUMENT;
            }
            ResultPack stResult = {g_nRightCount, g_nCBCount};
            pOutBuffer->Copy(&stResult, sizeof(stResult));
            g_nCBCount = 0;
            g_nRightCount = 0;

            break;
        }

        default:{
            return E_NOT_IMPLEMENTED;
        }
    }

    return ec;
}
